6.4 表达式
方法和函数一样,除直接调用外,还可赋值给变量,或作为参数传递。依照具体引用方式的不同,可分为expression和value两种状态。
Method Expression
通过类型引用的method expression会被还原为普通函数样式,receiver是第一参数,调用时须显式传参。至于类型,可以是T或*\T,只要目标方法存在于该类型方法集中即可。
type N int
func(n N)test() {
fmt.Printf("test.n: %p, %d\n", &n,n)
}
func main() {
var n N=25
fmt.Printf("main.n: %p, %d\n", &n,n)
f1:=N.test //func(n N)
f1(n)
f2:= (*N).test //func(n*N)
f2(&n) // 按方法集中的签名传递正确类型的参数
}输出:
main.n:0xc82000a140,25
test.n:0xc82000a158,25
test.n:0xc82000a168,25
尽管*\N方法集包装的test方法receiver类型不同,但编译器会保证按原定义类型拷贝传值。
当然,也可直接以表达式方式调用。
func main() {
var n N=25
N.test(n)
(*N).test(&n) // 注意: *N须使用括号,以免语法解析错误
}
Method Value
基于实例或指针引用的method value,参数签名不会改变,依旧按正常方式调用。
但当method value被赋值给变量或作为参数传递时,会立即计算并复制该方法执行所需的receiver对象,与其绑定,以便在稍后执行时,能隐式传入receiver参数。
type N int
func(n N)test() {
fmt.Printf("test.n: %p, %v\n", &n,n)
}
func main() {
var n N=100
p:= &n
n++
f1:=n.test // 因为test方法的receiver是N类型,
// 所以复制n,等于101
n++
f2:=p.test // 复制 *p,等于102
n++
fmt.Printf("main.n: %p, %v\n",p,n)
f1()
f2()
}
输出:
main.n:0xc820076028,103
test.n:0xc820076060,101
test.n:0xc820076070,102
编译器会为method value生成一个包装函数,实现间接调用。至于receiver复制,和闭包的实现方法基本相同,打包成funcval,经由DX寄存器传递。
当method value作为参数时,会复制含receiver在内的整个method value。
func call(m func()) {
m()
}
func main() {
var n N=100
p:= &n
fmt.Printf("main.n: %p, %v\n",p,n)
n++
call(n.test)
n++
call(p.test)
}
输出:
main.n:0xc82000a288,100
test.n:0xc82000a2c0,101
test.n:0xc82000a2d0,102
当然,如果目标方法的receiver是指针类型,那么被复制的仅是指针。
type N int
func(n*N)test() {
fmt.Printf("test.n: %p, %v\n",n, *n)
}
func main() {
var n N=100
p:= &n
n++
f1:=n.test // 因为test方法的receiver是 *N类型,
// 所以复制 &n
n++
f2:=p.test // 复制p指针
n++
fmt.Printf("main.n: %p, %v\n",p,n)
f1() // 延迟调用,n==103
f2()
}输出:
main.n:0xc82000a298,103
test.n:0xc82000a298,103
test.n:0xc82000a298,103
只要receiver参数类型正确,使用nil同样可以执行。
type N int
func(N)value() {}
func(*N)pointer() {}
func main() {
var p*N
p.pointer() //method value
(*N)(nil).pointer() //method value
(*N).pointer(nil) //method expression
//p.value() // 错误:invalid memory address or nil pointer dereference
}